package cn.sc.summer.exception.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import cn.sc.summer.constant.util.ConvertUtil;
import cn.sc.summer.exception.base.BaseException;
import cn.sc.summer.exception.enums.BaseExceptionEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.*;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.util.UriComponentsBuilder;

import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * 类名：restTemplate远程访问工具类
 *
 * @author a-xin
 * @date 2024/1/17 15:19
 */
@Slf4j
@Component
public class RestUtil {

    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate(LocalHttpUtil.clientHttpRequestFactory(2, 2));
        MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
        mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Arrays.asList(
                MediaType.TEXT_HTML,
                MediaType.TEXT_PLAIN,
                MediaType.MULTIPART_FORM_DATA));
        restTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter);
        restTemplate.getInterceptors().add(new ClientHttpRequestInterceptorX());
        return restTemplate;
    }

    /**
     * 发送PUT请求
     *
     * @param url     请求地址
     * @param headers 请求头信息
     * @param params  参数信息
     * @param clsType 返回参数映射类型
     * @param <T>     返回参数泛型
     * @return 返回数据body
     */
    public static <T> T put(String url, Map<String, String> headers, Map<String, String> params, TypeReference<T> clsType) {
        return exchange(url, HttpMethod.PUT, headers, params, clsType);
    }

    /**
     * 发送PUT请求
     *
     * @param url     请求地址
     * @param headers 请求头信息
     * @param params  参数信息
     * @return 返回数据body
     */
    public static String put(String url, Map<String, String> headers, Map<String, String> params) {
        return exchange(url, HttpMethod.PUT, headers, params);
    }

    /**
     * 发送POST请求
     *
     * @param url     请求地址
     * @param headers 请求头信息
     * @param params  参数信息
     * @param clsType 返回参数映射类型
     * @param <T>     返回参数泛型
     * @return 返回数据body
     */
    public static <T> T post(String url, Map<String, String> headers, Map<String, String> params, TypeReference<T> clsType) {
        return exchange(url, HttpMethod.POST, headers, params, clsType);
    }

    /**
     * 发送POST请求
     *
     * @param url     请求地址
     * @param headers 请求头信息
     * @param params  参数信息
     * @return 返回数据body
     */
    public static String post(String url, Map<String, String> headers, Map<String, String> params) {
        return exchange(url, HttpMethod.POST, headers, params);
    }

    /**
     * 发送GET请求
     *
     * @param url     请求地址
     * @param headers 请求头信息
     * @param params  参数信息
     * @param clsType 返回参数映射类型
     * @param <T>     返回参数泛型
     * @return 返回数据body
     */
    public static <T> T get(String url, Map<String, String> headers, Map<String, String> params, TypeReference<T> clsType) {
        return exchange(getGetParamUrl(url, params), HttpMethod.GET, headers, params, clsType);
    }

    /**
     * 发送GET请求
     *
     * @param url     请求地址
     * @param headers 请求头信息
     * @param params  参数信息
     * @return 返回数据body
     */
    public static String get(String url, Map<String, String> headers, Map<String, String> params) {
        return exchange(getGetParamUrl(url, params), HttpMethod.GET, headers, params);
    }

    /**
     * 上传文件
     *
     * @param url     请求地址
     * @param fileKey 文件请求字段名称
     * @param file    文件信息
     * @return 返回数据body
     */
    public static String uploadFile(String url, String fileKey, MultipartFile file) {
        try {
            RestTemplate restTemplate = SpringUtil.getBean(RestTemplate.class);
            MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
            InputStreamResource inputStreamResource = new InputStreamResource(file.getInputStream()) {
                @Override
                public String getFilename() {
                    return file.getOriginalFilename();
                }

                @Override
                public long contentLength() {
                    return file.getSize();
                }
            };
            params.add(fileKey, inputStreamResource);
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.MULTIPART_FORM_DATA);
            HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(params, headers);
            ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
            BaseExceptionEnum.NORMAL_EXCEPTION.exceptionIf(!responseEntity.getStatusCode().equals(HttpStatus.OK), responseEntity.getBody());
            return responseEntity.getBody();
        } catch (Exception e) {
            throw new BaseException(e.getMessage());
        }
    }

    /**
     * 下载文件，直接返回到response中，没有中转文件数据
     *
     * @param url        请求地址
     * @param httpMethod 请求方式
     * @param params     参数信息
     * @param response   接收数据返回载体
     */
    public static void downloadFile(String url, HttpMethod httpMethod, Map<String, String> params, HttpServletResponse response) {
        Map<String, String> uriVariables = new HashMap<>();
        if (CollUtil.isNotEmpty(params)) {
            uriVariables.putAll(params);
        }
        ResponseExtractor<Boolean> responseExtractor = clientHttpResponse -> {
            // 设置响应头，直接用第三方文件服务的响应头
            HttpHeaders headers = clientHttpResponse.getHeaders();
            headers.forEach((key, value) -> response.setHeader(key, value.get(0)));
            // 收到响应输入流即时拷贝写出到响应输出流中: inputStream -> outputStream
            StreamUtils.copy(clientHttpResponse.getBody(), response.getOutputStream());
            return true;
        };
        RestTemplate restTemplate = SpringUtil.getBean(RestTemplate.class);
        restTemplate.execute(url, httpMethod, null, responseExtractor, uriVariables);
    }


    /**
     * restTemplate请求工具主类
     *
     * @param url        请求地址
     * @param httpMethod 请求方式
     * @param headers    请求头信息
     * @param params     参数信息
     * @param clsType    返回参数映射类型
     * @param <T>        返回参数泛型
     * @return 返回数据body
     */
    private static <T> T exchange(String url, HttpMethod httpMethod, Map<String, String> headers, Map<String, String> params, TypeReference<T> clsType) {
        return ConvertUtil.toObject(ConvertUtil.xmlToJsonStr(exchange(url, httpMethod, headers, params)), clsType);
    }

    /**
     * restTemplate请求工具主类
     *
     * @param url        请求地址
     * @param httpMethod 请求方式
     * @param headers    请求头信息
     * @param params     参数信息
     * @return 返回数据body
     */
    private static String exchange(String url, HttpMethod httpMethod, Map<String, String> headers, Map<String, String> params) {
        RestTemplate restTemplate = SpringUtil.getBean(RestTemplate.class);
        MultiValueMap<String, String> headersMap = new LinkedMultiValueMap<>();
        if (CollUtil.isNotEmpty(headers)) {
            headersMap.setAll(headers);
        }
        MultiValueMap<String, String> bodyMap = new LinkedMultiValueMap<>();
        if (CollUtil.isNotEmpty(params)) {
            bodyMap.setAll(params);
        }
        HttpEntity<MultiValueMap<String, String>> multiValueMapHttpEntity = new HttpEntity<>(bodyMap, headersMap);
        ResponseEntity<String> responseEntity = restTemplate.exchange(url, httpMethod, multiValueMapHttpEntity, String.class);
        BaseExceptionEnum.NORMAL_EXCEPTION.exceptionIf(!responseEntity.getStatusCode().equals(HttpStatus.OK), responseEntity.getBody());
        return responseEntity.getBody();
    }

    /**
     * 格式化get请求地址
     *
     * @param url    访问地址
     * @param params 参数信息
     * @return 组装好的请求地址
     */
    private static String getGetParamUrl(@NonNull String url, Map<String, String> params) {
        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
        params.entrySet().stream().forEach(o -> builder.queryParam(o.getKey(), o.getValue()));
        return builder.build().encode().toString();
    }

}
